// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: http://codemirror.net/LICENSE

(function(mod) {
  if (typeof exports == 'object' && typeof module == 'object')
    // CommonJS
    mod(require('../../lib/codemirror'));
  else if (typeof define == 'function' && define.amd)
    // AMD
    define(['../../lib/codemirror'], mod);
  // Plain browser env
  else mod(CodeMirror);
})(function(CodeMirror) {
  'use strict';

  CodeMirror.defineMode('fortran', function() {
    function words(array) {
      var keys = {};
      for (var i = 0; i < array.length; ++i) {
        keys[array[i]] = true;
      }
      return keys;
    }

    var keywords = words([
      'abstract',
      'accept',
      'allocatable',
      'allocate',
      'array',
      'assign',
      'asynchronous',
      'backspace',
      'bind',
      'block',
      'byte',
      'call',
      'case',
      'class',
      'close',
      'common',
      'contains',
      'continue',
      'cycle',
      'data',
      'deallocate',
      'decode',
      'deferred',
      'dimension',
      'do',
      'elemental',
      'else',
      'encode',
      'end',
      'endif',
      'entry',
      'enumerator',
      'equivalence',
      'exit',
      'external',
      'extrinsic',
      'final',
      'forall',
      'format',
      'function',
      'generic',
      'go',
      'goto',
      'if',
      'implicit',
      'import',
      'include',
      'inquire',
      'intent',
      'interface',
      'intrinsic',
      'module',
      'namelist',
      'non_intrinsic',
      'non_overridable',
      'none',
      'nopass',
      'nullify',
      'open',
      'optional',
      'options',
      'parameter',
      'pass',
      'pause',
      'pointer',
      'print',
      'private',
      'program',
      'protected',
      'public',
      'pure',
      'read',
      'recursive',
      'result',
      'return',
      'rewind',
      'save',
      'select',
      'sequence',
      'stop',
      'subroutine',
      'target',
      'then',
      'to',
      'type',
      'use',
      'value',
      'volatile',
      'where',
      'while',
      'write',
    ]);
    var builtins = words([
      'abort',
      'abs',
      'access',
      'achar',
      'acos',
      'adjustl',
      'adjustr',
      'aimag',
      'aint',
      'alarm',
      'all',
      'allocated',
      'alog',
      'amax',
      'amin',
      'amod',
      'and',
      'anint',
      'any',
      'asin',
      'associated',
      'atan',
      'besj',
      'besjn',
      'besy',
      'besyn',
      'bit_size',
      'btest',
      'cabs',
      'ccos',
      'ceiling',
      'cexp',
      'char',
      'chdir',
      'chmod',
      'clog',
      'cmplx',
      'command_argument_count',
      'complex',
      'conjg',
      'cos',
      'cosh',
      'count',
      'cpu_time',
      'cshift',
      'csin',
      'csqrt',
      'ctime',
      'c_funloc',
      'c_loc',
      'c_associated',
      'c_null_ptr',
      'c_null_funptr',
      'c_f_pointer',
      'c_null_char',
      'c_alert',
      'c_backspace',
      'c_form_feed',
      'c_new_line',
      'c_carriage_return',
      'c_horizontal_tab',
      'c_vertical_tab',
      'dabs',
      'dacos',
      'dasin',
      'datan',
      'date_and_time',
      'dbesj',
      'dbesj',
      'dbesjn',
      'dbesy',
      'dbesy',
      'dbesyn',
      'dble',
      'dcos',
      'dcosh',
      'ddim',
      'derf',
      'derfc',
      'dexp',
      'digits',
      'dim',
      'dint',
      'dlog',
      'dlog',
      'dmax',
      'dmin',
      'dmod',
      'dnint',
      'dot_product',
      'dprod',
      'dsign',
      'dsinh',
      'dsin',
      'dsqrt',
      'dtanh',
      'dtan',
      'dtime',
      'eoshift',
      'epsilon',
      'erf',
      'erfc',
      'etime',
      'exit',
      'exp',
      'exponent',
      'extends_type_of',
      'fdate',
      'fget',
      'fgetc',
      'float',
      'floor',
      'flush',
      'fnum',
      'fputc',
      'fput',
      'fraction',
      'fseek',
      'fstat',
      'ftell',
      'gerror',
      'getarg',
      'get_command',
      'get_command_argument',
      'get_environment_variable',
      'getcwd',
      'getenv',
      'getgid',
      'getlog',
      'getpid',
      'getuid',
      'gmtime',
      'hostnm',
      'huge',
      'iabs',
      'iachar',
      'iand',
      'iargc',
      'ibclr',
      'ibits',
      'ibset',
      'ichar',
      'idate',
      'idim',
      'idint',
      'idnint',
      'ieor',
      'ierrno',
      'ifix',
      'imag',
      'imagpart',
      'index',
      'int',
      'ior',
      'irand',
      'isatty',
      'ishft',
      'ishftc',
      'isign',
      'iso_c_binding',
      'is_iostat_end',
      'is_iostat_eor',
      'itime',
      'kill',
      'kind',
      'lbound',
      'len',
      'len_trim',
      'lge',
      'lgt',
      'link',
      'lle',
      'llt',
      'lnblnk',
      'loc',
      'log',
      'logical',
      'long',
      'lshift',
      'lstat',
      'ltime',
      'matmul',
      'max',
      'maxexponent',
      'maxloc',
      'maxval',
      'mclock',
      'merge',
      'move_alloc',
      'min',
      'minexponent',
      'minloc',
      'minval',
      'mod',
      'modulo',
      'mvbits',
      'nearest',
      'new_line',
      'nint',
      'not',
      'or',
      'pack',
      'perror',
      'precision',
      'present',
      'product',
      'radix',
      'rand',
      'random_number',
      'random_seed',
      'range',
      'real',
      'realpart',
      'rename',
      'repeat',
      'reshape',
      'rrspacing',
      'rshift',
      'same_type_as',
      'scale',
      'scan',
      'second',
      'selected_int_kind',
      'selected_real_kind',
      'set_exponent',
      'shape',
      'short',
      'sign',
      'signal',
      'sinh',
      'sin',
      'sleep',
      'sngl',
      'spacing',
      'spread',
      'sqrt',
      'srand',
      'stat',
      'sum',
      'symlnk',
      'system',
      'system_clock',
      'tan',
      'tanh',
      'time',
      'tiny',
      'transfer',
      'transpose',
      'trim',
      'ttynam',
      'ubound',
      'umask',
      'unlink',
      'unpack',
      'verify',
      'xor',
      'zabs',
      'zcos',
      'zexp',
      'zlog',
      'zsin',
      'zsqrt',
    ]);

    var dataTypes = words([
      'c_bool',
      'c_char',
      'c_double',
      'c_double_complex',
      'c_float',
      'c_float_complex',
      'c_funptr',
      'c_int',
      'c_int16_t',
      'c_int32_t',
      'c_int64_t',
      'c_int8_t',
      'c_int_fast16_t',
      'c_int_fast32_t',
      'c_int_fast64_t',
      'c_int_fast8_t',
      'c_int_least16_t',
      'c_int_least32_t',
      'c_int_least64_t',
      'c_int_least8_t',
      'c_intmax_t',
      'c_intptr_t',
      'c_long',
      'c_long_double',
      'c_long_double_complex',
      'c_long_long',
      'c_ptr',
      'c_short',
      'c_signed_char',
      'c_size_t',
      'character',
      'complex',
      'double',
      'integer',
      'logical',
      'real',
    ]);
    var isOperatorChar = /[+\-*&=<>\/\:]/;
    var litOperator = new RegExp(
      '(.and.|.or.|.eq.|.lt.|.le.|.gt.|.ge.|.ne.|.not.|.eqv.|.neqv.)',
      'i',
    );

    function tokenBase(stream, state) {
      if (stream.match(litOperator)) {
        return 'operator';
      }

      var ch = stream.next();
      if (ch == '!') {
        stream.skipToEnd();
        return 'comment';
      }
      if (ch == '"' || ch == "'") {
        state.tokenize = tokenString(ch);
        return state.tokenize(stream, state);
      }
      if (/[\[\]\(\),]/.test(ch)) {
        return null;
      }
      if (/\d/.test(ch)) {
        stream.eatWhile(/[\w\.]/);
        return 'number';
      }
      if (isOperatorChar.test(ch)) {
        stream.eatWhile(isOperatorChar);
        return 'operator';
      }
      stream.eatWhile(/[\w\$_]/);
      var word = stream.current().toLowerCase();

      if (keywords.hasOwnProperty(word)) {
        return 'keyword';
      }
      if (builtins.hasOwnProperty(word) || dataTypes.hasOwnProperty(word)) {
        return 'builtin';
      }
      return 'variable';
    }

    function tokenString(quote) {
      return function(stream, state) {
        var escaped = false,
          next,
          end = false;
        while ((next = stream.next()) != null) {
          if (next == quote && !escaped) {
            end = true;
            break;
          }
          escaped = !escaped && next == '\\';
        }
        if (end || !escaped) state.tokenize = null;
        return 'string';
      };
    }

    // Interface

    return {
      startState: function() {
        return { tokenize: null };
      },

      token: function(stream, state) {
        if (stream.eatSpace()) return null;
        var style = (state.tokenize || tokenBase)(stream, state);
        if (style == 'comment' || style == 'meta') return style;
        return style;
      },
    };
  });

  CodeMirror.defineMIME('text/x-fortran', 'fortran');
});
